/********************************************************************************
 *                               libemu
 *
 *                    - x86 shellcode emulation -
 *
 *
 * Copyright (C) 2007  Paul Baecher & Markus Koetter
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * 
 *             contact nepenthesdev@users.sourceforge.net  
 *
 *******************************************************************************/

#include <emu/emu.h>
#include <emu/emu_memory.h>
#include <emu/emu_cpu.h>
#include <emu/emu_log.h>
#include <emu/emu_cpu_data.h>
#include <emu/emu_cpu_stack.h>
#include <emu/environment/emu_profile.h>
#include <emu/environment/emu_env.h>
#include <emu/environment/win32/emu_env_w32.h>
#include <emu/environment/win32/emu_env_w32_dll.h>
#include <emu/environment/win32/emu_env_w32_dll_export.h>
#include <emu/environment/win32/env_w32_dll_export_kernel32_hooks.h>
#include <emu/environment/linux/emu_env_linux.h>
#include <emu/emu_getpc.h>
#include <emu/emu_graph.h>
#include <emu/emu_string.h>
#include <emu/emu_hashtable.h>

#include <emu/emu_shellcode.h>


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/select.h>

#include <sys/wait.h>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif


#include <stdint.h>

#define HAVE_UNISTD
#ifdef HAVE_UNISTD
# include <unistd.h>
#endif
#include <stdio.h>

#include <stdarg.h>


#include <errno.h>



#define CODE_OFFSET 0x417000

#define F(x) (1 << (x))

uint32_t opts_steps;
unsigned char *opts_scode;
uint32_t opts_size;
int opts_offset;

//#include "options.h"


//struct run_time_options opts;

/*
static const char *regm[] = {
	"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
};


// 0     1     2     3      4       5       6     7 
static const char *flags[] = { "CF", "  ", "PF", "  " , "AF"  , "    ", "ZF", "SF", 
	"TF", "IF", "DF", "OF" , "IOPL", "IOPL", "NT", "  ",
	"RF", "VM", "AC", "VIF", "RIP" , "ID"  , "  ", "  ",
	"  ", "  ", "  ", "   ", "    ", "    ", "  ", "  "};
*/

int graph_draw(struct emu_graph *graph);


uint32_t user_hook_URLDownloadToFile(struct emu_env *env, struct emu_env_hook *hook, ...)
{
	printf("Hook me Captain Cook!\n");
	printf("%s:%i %s\n",__FILE__,__LINE__,__FUNCTION__);

	va_list vl;
	va_start(vl, hook);

	/*void * pCaller    = */(void)va_arg(vl, void *);
	char * szURL      = va_arg(vl, char *);
	char * szFileName = va_arg(vl, char *);
	/*int    dwReserved = */(void)va_arg(vl, int   );
	/*void * lpfnCB     = */(void)va_arg(vl, void *);

	va_end(vl);

        void (*addurl)(char *);

        addurl = hook->hook.win->userdata;
        
	printf("download %s -> %s\n", szURL, szFileName);

	return 0;
}


int test(struct emu *e)
{
//	int i=0;
	struct emu_cpu *cpu = emu_cpu_get(e);
	struct emu_memory *mem = emu_memory_get(e);

	struct emu_env *env = emu_env_new(e);
	env->profile = emu_profile_new();


	/* IAT for sqlslammer */
	emu_memory_write_dword(mem, 0x42AE1018, 0x7c801D77);
	emu_memory_write_dword(mem, 0x42ae1010, 0x7c80ADA0);
	emu_memory_write_dword(mem, 0x7c80ADA0, 0x51EC8B55);

	if ( env == NULL )
	{
		fprintf(stderr,"%s \n", emu_strerror(e));
		fprintf(stderr,"%s \n", strerror(emu_errno(e)));
		return -1;
	}

	//emu_env_w32_load_dll(env->env.win, "msvcrt.dll");
	//emu_env_w32_load_dll(env->env.win, "ws2_32.dll");
	emu_env_w32_load_dll(env->env.win, "urlmon.dll");
        emu_env_w32_export_hook(env, "URLDownloadToFileA", user_hook_URLDownloadToFile, NULL);


	int j=0;

	/* run the code */
        /* emu_log_level_set(emu_logging_get(e),EMU_LOG_DEBUG); */
        /* emu_cpu_debug_print(cpu); */
        /* emu_log_level_set(emu_logging_get(e),EMU_LOG_NONE); */

	int ret; //= emu_cpu_run(emu_cpu_get(e));

	uint32_t eipsave = 0;
	for ( j=0;j<opts_steps;j++ )
	{
                /* emu_log_level_set(emu_logging_get(e),EMU_LOG_DEBUG); */
                /* emu_cpu_debug_print(cpu); */
                /* emu_log_level_set(emu_logging_get(e),EMU_LOG_NONE); */

		if ( cpu->repeat_current_instr == false )
			eipsave = emu_cpu_eip_get(emu_cpu_get(e));

		struct emu_env_hook *hook = NULL;

		ret = 0;

		hook = emu_env_w32_eip_check(env);

		if ( hook != NULL )
		{
                        if ( hook->hook.win->fnhook == NULL )
			{
				fprintf(stderr,"unhooked call to %s\n", hook->hook.win->fnname);
				break;
			}
                        
		}
		else
		{

			ret = emu_cpu_parse(emu_cpu_get(e));
                        
                        emu_log_level_set(emu_logging_get(e),EMU_LOG_DEBUG);
                        logDebug(e, "%s\n", cpu->instr_string);
                        emu_log_level_set(emu_logging_get(e),EMU_LOG_NONE);

			struct emu_env_hook *hook =NULL;

			if ( ret != -1 )
			{
				if ( hook == NULL )
				{
                                        emu_log_level_set(emu_logging_get(e),EMU_LOG_DEBUG);
                                        ret = emu_cpu_step(emu_cpu_get(e));
                                        emu_log_level_set(emu_logging_get(e),EMU_LOG_NONE);
				}
				else
				{
					if ( hook->hook.lin->fnhook != NULL )
						hook->hook.lin->fnhook(env, hook);
					else
						break;
				}
			}

			if ( ret == -1 )
			{
				printf("cpu error %s\n", emu_strerror(e));
				break;
			}
		}
	}

	printf("stepcount %i\n",j);

        
        /* emu_log_level_set(emu_logging_get(e),EMU_LOG_DEBUG); */
        /* emu_cpu_debug_print(cpu); */
        /* emu_log_level_set(emu_logging_get(e),EMU_LOG_NONE); */

	emu_profile_debug(env->profile);//Print profile

        //emu_profile_dump(env->profile, "scprofile.dump");

	return 0;
}




int getpctest(void)
{
	struct emu *e = emu_new();

	if ( (opts_offset = emu_shellcode_test(e, (uint8_t *)opts_scode, opts_size)) >= 0 )
		printf("%s offset = 0x%08x\n","SUCCESS", opts_offset);
	else
		printf("FAILED\n");

	emu_free(e);

	return 0;
}

int prepare_from_stdin_read(void)
{
	unsigned buffer[BUFSIZ];
	int ret, eof=0;
	int16_t bytes_read=0;
	uint32_t len=0;
	fd_set read_fds;
	struct timeval st;

	while ( !eof )
	{
		FD_ZERO(&read_fds);
		FD_SET(STDIN_FILENO, &read_fds);

		st.tv_sec  = 10;
		st.tv_usec = 0;

		switch ( ret = select(FD_SETSIZE, &read_fds, NULL, NULL, &st) )
		{
		case -1:
			fprintf(stderr, "Error with select(): %s.\n", strerror(errno));
			exit(1);
		case  0:
			break;
		default:
			if ( FD_ISSET(STDIN_FILENO, &read_fds) )
			{
				if ( (bytes_read = read(STDIN_FILENO, buffer, BUFSIZ)) <= 0 )
				{
					if ( bytes_read == 0 ) eof = 1;
					else
					{
						fprintf(stderr, "Error while reading data: %s.\n", strerror(errno));
						exit(1);
					}
				}
				if ( !eof )
				{
					if ( (opts_scode = (unsigned char *) realloc(opts_scode, len+bytes_read)) == NULL )
					{
						fprintf(stderr, "Error while allocating memory: %s.\n", strerror(errno));
						exit(1);
					}
					memcpy(opts_scode+len, buffer, bytes_read);
					len += bytes_read;
				}
			}
		}
	}
	opts_size = len;

	return 0;
}




int prepare_from_stdin_write(struct emu *e)
{
	/* set the registers to the initial values */
	struct emu_cpu *cpu = emu_cpu_get(e);
	struct emu_memory *mem = emu_memory_get(e);

	int j;
	for ( j=0;j<8;j++ )
	{
		emu_cpu_reg32_set(cpu,j , 0);
	}

	emu_memory_write_dword(mem, 0xef787c3c,  4711);
	emu_memory_write_dword(mem, 0x0,  4711);
	emu_memory_write_dword(mem, 0x00416f9a,  4711);
	emu_memory_write_dword(mem, 0x0044fcf7, 4711);
	emu_memory_write_dword(mem, 0x00001265, 4711);
	emu_memory_write_dword(mem, 0x00002583, 4711);
	emu_memory_write_dword(mem, 0x00e000de, 4711);
	emu_memory_write_dword(mem, 0x01001265, 4711);
	emu_memory_write_dword(mem, 0x8a000066, 4711);

	/* set the flags */
	emu_cpu_eflags_set(cpu, 0);


	/* write the code to the offset */
	int static_offset = CODE_OFFSET;
	emu_memory_write_block(mem, static_offset, opts_scode,  opts_size);



	/* set eip to the code */
	emu_cpu_eip_set(emu_cpu_get(e), static_offset + opts_offset);

	emu_memory_write_block(mem, 0x0012fe98, opts_scode,  opts_size);
	emu_cpu_reg32_set(emu_cpu_get(e), esp, 0x0012fe98);

//	free(opts.scode);

	return 0;
}

int prepare_from_stdin(struct emu *e)
{
	if (opts_size == 0)
		prepare_from_stdin_read();

	prepare_from_stdin_write(e);
	return 0;

}

int prepare(struct emu *emu)
{
        return prepare_from_stdin(emu);
}


int main(int argc, char *argv[])
{
	opts_steps = 100000;
	opts_offset = 0xf8;
        
	struct emu *e = emu_new();
	if ( prepare(e) == 0 )
	{
		printf("DEBUG:%s offset = 0x%08x\n","SUCCESS", opts_offset);
                getpctest();
		printf("DEBUG:%s offset = 0x%08x\n","SUCCESS", opts_offset);
                
                emu_free(e);
                e = emu_new();
                prepare(e);
		test(e);
	}

	emu_free(e);
//	dump_export_table();

	if (opts_scode)
		free(opts_scode);

	return 0;
}


